import { EditorGlassInspector } from './EditorGlassInspector';
import { EditorBlock } from './EditorBlock';
import { _decorator, Node, color, KeyCode } from 'cc';
import { BaseView } from '../../../../scripts/libs/rxjs/cc3/BaseView';
import { shapeInfoMap, StageInfo } from '../../game/basic';
import { Color } from 'cc';
import { v3 } from 'cc';
import { find } from 'cc';
import { v2 } from 'cc';
import { Vec3 } from 'cc';
import { Prefab } from 'cc';
import { instantiate } from 'cc';
import { BindNodeTouchWithClick, touchLocationToWorldPosition } from '../../../../scripts/libs/rxjs/cc3/BindNode';
import { lm } from '../../LobbyManager';
import { EventTouch } from 'cc';
import { gm } from '../../../../scripts/managers/GameManager';
import { UITransform } from 'cc';
import { isValid } from 'cc';
import { KeyDown } from '../../../../scripts/libs/rxjs/cc3/KeyEvent';
import { EditorBlockInspector } from './EditorBlockInspector';
import { ReactiveProperty } from '../../../../scripts/libs/rxjs/cc3/ReactiveProperty';
import { Observable, Subscription } from 'rxjs';
import { TweenToStart } from '../../../../scripts/libs/rxjs/cc3/TweenStart';
import { nc } from '../../../../scripts/libs/rxjs/NotificationCenter';
import { EditorGlass } from './EditorGlass';
import { EditorGlassExplorer } from './EditorGlassExplorer';
const { ccclass, property } = _decorator;

const _v20 = v2();
const _v30 = v3();

@ccclass('EditorStageView')
export class EditorStageView extends BaseView {

    _color = color().fromHEX('#c0491f');
    get logColorOverride(): Color {
        return this._color;
    }

    @property(Prefab)
    editorGroundPrefab: Prefab = null;

    @property(Prefab)
    editorIndicatorPrefab: Prefab = null;

    @property(Prefab)
    editorBlockPrefab: Prefab = null;

    @property(Prefab)
    editorBlockInspectorPrefab: Prefab = null;

    @property(Prefab)
    editorGlassPrefab: Prefab = null;

    @property(Prefab)
    editorGlassInspectorPrefab: Prefab = null;

    @property(Prefab)
    editorGlassExplorerPrefab: Prefab = null;

    @property(Prefab)
    editorObstaclePrefab: Prefab = null;

    editorIndicator: Node = null;
    indicatorContainer: Node = null;

    stageInfo: StageInfo = null;

    groundContainer: Node = null;
    glassContainer: Node = null;
    blockContainer: Node = null;

    inspectorContainer: Node = null;

    blockInspectorMap = new Map<number, EditorBlockInspector>();
    glassInspectorMap = new Map<number, EditorGlassInspector>();

    get TotalCellCount() {
        if (this.stageInfo != null) {
            return this.stageInfo.width * this.stageInfo.height;
        }
        return 0;
    }

    willBind(): void {
        globalThis.stage2d = this;
        this.groundContainer = find('groundContainer', this.node);
        this.glassContainer = find('glassContainer', this.node);
        this.blockContainer = find('blockContainer', this.node);
        this.indicatorContainer = find('indicatorContainer', this.node);
    }

    colorSelectedStr: string = null;

    useObserves() {
        return [
            BindNodeTouchWithClick(this.node, lm.uiRoot.camera).subscribe(this.handleTouch.bind(this)),
            lm.touchGame.subscribe(eventTouch => {
                eventTouch.getLocation(_v20);
                touchLocationToWorldPosition(lm.uiRoot.camera, _v20, _v30);
                if (eventTouch.type == 'touch-start') {
                } else {
                    this.handleTouch({ type: eventTouch.type, eventTouch, worldPosition: _v30 })
                }
            }),
            gm.editPosSelected.subscribe(_ => {
                if (_ != null) {
                    if (!isValid(this.editorIndicator)) {
                        this.editorIndicator = instantiate(this.editorIndicatorPrefab);
                        this.editorIndicator.setParent(this.indicatorContainer);
                    }
                    this.editorIndicator.setPosition(this.getPositionByIdx(_));
                } else if (_ == null && isValid(this.editorIndicator)) {
                    this.editorIndicator.destroy();
                    this.editorIndicator = null;
                }

                // this.tryOpenBlockInspector();
            }),
            KeyDown().subscribe((_: KeyCode) => {
                if (_ == KeyCode.KEY_Z && gm.editCtrlPressed.value) {
                    this.undo();
                } else if (_ == KeyCode.ESCAPE) {
                    gm.editPosSelected.value = null;
                    this.closeAllInspectors();
                    gm.unfocusAllCommand.execute();
                } else if (_ == KeyCode.BACKSPACE || _ == KeyCode.DELETE) {
                    if (gm.editPosSelected.value != null) {
                        this.removeBlock(gm.editPosSelected.value);
                        this.remakeStageInfo();
                    }
                } else if (_ == KeyCode.NUM_0 || _ == KeyCode.DIGIT_0) {
                    // gm.editColorSelected.value = 0;
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "0";
                    else this.colorSelectedStr = "0";
                } else if (_ == KeyCode.NUM_1 || _ == KeyCode.DIGIT_1) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "1";
                    else gm.editColorSelected.value = 1;
                } else if (_ == KeyCode.NUM_2 || _ == KeyCode.DIGIT_2) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "2"
                    else gm.editColorSelected.value = 2;
                } else if (_ == KeyCode.NUM_3 || _ == KeyCode.DIGIT_3) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "3"
                    else gm.editColorSelected.value = 3;
                } else if (_ == KeyCode.NUM_4 || _ == KeyCode.DIGIT_4) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "4"
                    else gm.editColorSelected.value = 4;
                } else if (_ == KeyCode.NUM_5 || _ == KeyCode.DIGIT_5) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "5"
                    else gm.editColorSelected.value = 5;
                } else if (_ == KeyCode.NUM_6 || _ == KeyCode.DIGIT_6) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "6"
                    else gm.editColorSelected.value = 6;
                } else if (_ == KeyCode.NUM_7 || _ == KeyCode.DIGIT_7) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "7"
                    else gm.editColorSelected.value = 7;
                } else if (_ == KeyCode.NUM_8 || _ == KeyCode.DIGIT_8) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "8"
                    else gm.editColorSelected.value = 8;
                } else if (_ == KeyCode.NUM_9 || _ == KeyCode.DIGIT_9) {
                    if (this.colorSelectedStr != null) this.colorSelectedStr += "9"
                    else gm.editColorSelected.value = 9;
                }
                if (this.colorSelectedStr?.length >= 3) {
                    gm.editColorSelected.value = Number(this.colorSelectedStr);
                    this.colorSelectedStr = null;
                }
            }),
            nc.subscribe<{ shapeId: number }>('create_shape', _ => {
                let { shapeId } = _;
                this.log('@create_shape', gm.editToolsSelected.value, shapeId, gm.editColorSelected.value);

                if (gm.editToolsSelected.value == 'block') {
                    let editorBlock = this.addBlock(0, shapeId);
                    this.draggedEditorBlock.value = editorBlock;
                } else if (gm.editToolsSelected.value == 'glass') {
                    let editorGlass = this.addGlass(0, shapeId);
                    this.draggedEditorGlass.value = editorGlass;
                }

            }),

            gm.editCtrlPressed.subscribe(_ => {
                for (let [glassId, editorGlass] of this.glassMap) {
                    if (this.glassInspectorMap.has(glassId)) {
                        editorGlass.node.active = true;
                    } else {
                        editorGlass.node.active = !_;
                    }
                }
            })
        ];
    }

    static Space = v2(100, 100);

    static getPosByOffsetRowCol(row: number, col: number) {
        return v3(col * this.Space.x, -row * this.Space.y, 0);
    }

    get Width(): number {
        return this.stageInfo?.width ?? 0;
    }

    get Height(): number {
        return this.stageInfo?.height ?? 0;
    }

    getIdxByRowCol(rc: number[]) {
        return rc[0] * this.Width + rc[1];
    }
    getRowColPos(idx: number): number[] {
        return [Math.floor(idx / this.Width), idx % this.Width]; // 得到行列坐标，上到下，左到右
    }
    getPositionByIdx(idx: number) {
        let [row, col] = this.getRowColPos(idx);
        return this.getPositionByRowCol(row, col);
    }
    getPositionByRowCol(row: number, col: number) {
        let x = col * EditorStageView.Space.x - EditorStageView.Space.x * this.Width / 2 + EditorStageView.Space.x / 2;
        let y = row * -EditorStageView.Space.y + EditorStageView.Space.y * this.Height / 2 - EditorStageView.Space.y / 2;
        return v3(x, y, 0);
    }
    getRowColByPos(position: Vec3) {
        let x = Math.floor((position.x + EditorStageView.Space.x * this.Width / 2) / EditorStageView.Space.x);
        let y = this.Height - Math.floor((position.y + EditorStageView.Space.y * this.Height / 2) / EditorStageView.Space.y) - 1;
        return [y, x];
    }
    getIdxByPos(position: Vec3) {
        let [row, col] = this.getRowColByPos(position);
        if (row < 0 || row >= this.Height) return null;
        if (col < 0 || col >= this.Width) return null;
        return this.getIdxByRowCol([row, col]);
    }
    getAdjoin1(posId) {
        let [row, col] = this.getRowColPos(posId);
        let list = [
            { data: [row, col + 1], direction: 0 },
            { data: [row - 1, col], direction: 1 },
            { data: [row, col - 1], direction: 2 },
            { data: [row + 1, col], direction: 3 }
        ];
        return list.filter(_ => {
            let [row, col] = _.data;
            return row >= 0 && row < this.Height && col >= 0 && col < this.Width;
        }).map(_ => {
            let [row, col] = _.data;
            return { idx: this.getIdxByRowCol([row, col]), direction: _.direction };
        });
    }
    genShapePos(posId: number, shapeId: number) { // 根据blockId计算block的中心位置
        let shapeInfo = shapeInfoMap.get(shapeId);
        let rcPos = this.getRowColPos(posId);
        rcPos[0] += shapeInfo.center[0];
        rcPos[1] += shapeInfo.center[1];
        return this.getPositionByRowCol(rcPos[0], rcPos[1]);
    }

    loadStage(stageInfo: StageInfo) {
        this.log('loadStage', stageInfo);
        this.stageInfo = stageInfo;
        this.closeAllInspectors(true);
        this.blockMap.clear();
        this.blockContainer.destroyAllChildren();
        this.glassMap.clear();
        this.glassContainer.destroyAllChildren();
        this.genGrids();
        this.closeGlassExplorer();
        this.tryOpenGlassExplorer();
    }

    genGrids() {
        this.groundContainer.destroyAllChildren();

        for (let i = 0; i < this.TotalCellCount; i++) {
            let groundNode = instantiate(this.editorGroundPrefab);
            groundNode.setParent(this.groundContainer);
            groundNode.setPosition(this.getPositionByIdx(i));
        }

        if (this.stageInfo.blocks == null) this.stageInfo.blocks = [];
        for (let i = 0; i < this.stageInfo.blocks.length; i++) {
            let blockInfo = this.stageInfo.blocks[i];
            let editorBlock = this.addBlock(blockInfo.pos, blockInfo.shape, blockInfo.id);
            editorBlock.blockId.value = blockInfo.id;
            editorBlock.colorId = blockInfo.color;
        }
        if (this.stageInfo.glasses == null) this.stageInfo.glasses = [];
        for (let i = 0; i < this.stageInfo.glasses.length; i++) {
            let glassInfo = this.stageInfo.glasses[i];
            let editorGlass = this.addGlass(glassInfo.pos, glassInfo.shape, glassInfo.id);
            editorGlass.glassId.value = glassInfo.id;
            editorGlass.nails.value = glassInfo.nails;
        }
    }

    _v30 = v3();

    draggedEditorBlock = ReactiveProperty.Create<EditorBlock>(null);
    draggedEditorGlass = ReactiveProperty.Create<EditorGlass>(null);

    draggedOffset = v3(0, 0, 0);

    _findGlassOffset = 0; // 可以选择不同层级的玻璃

    _lastType = null;

    handleTouch(param: { type: string, eventTouch: EventTouch, worldPosition: Vec3 }) {
        let { type, worldPosition } = param;
        if (gm.editAltPressed.value) {
            this.handleTouchInEditModePanStage(param);
            return;
        }

        switch (type) {
            case 'touch-start':
            case 'touch-move': {
                if (this.draggedEditorGlass.value == null) {
                    let editorBlock = this.findBlockByWorldPos(worldPosition);
                    if (this.draggedEditorBlock.value == null && editorBlock != null) {
                        this.draggedEditorBlock.value = editorBlock;
                        let position = this.getComponent(UITransform).convertToNodeSpaceAR(worldPosition);
                        this.draggedOffset.set(position).subtract(editorBlock.node.position);
                        // gm.editSelectedBlockView.value = blockView;
                        this.tryOpenBlockInspector(editorBlock.blockId.value);
                    }
                    if (this.draggedEditorBlock.value != null) {
                        let position = this.getComponent(UITransform).convertToNodeSpaceAR(worldPosition);
                        let targetPosition = v3(position).subtract(this.draggedOffset);
                        this._v30.set(targetPosition);
                        this.draggedEditorBlock.value.node.setPosition(this._v30);
                        this.blockInspectorMap.get(this.draggedEditorBlock.value.blockId.value)?.refreshGraphics();
                    }
                }
                if (this.draggedEditorBlock.value == null) {
                    let editorGlasses = this.findGlassByWorldPos(worldPosition);
                    let editorGlass = null;
                    if (gm.editShiftPressed.value && type == 'touch-start') {
                        this._findGlassOffset++;
                    }
                    if (!gm.editShiftPressed.value) { // 优先选择带有 inspector的
                        editorGlass = editorGlasses.filter(_ => this.glassInspectorMap.has(_.glassId.value))?.[this._findGlassOffset % editorGlasses.length];
                    }
                    if (editorGlass == null) {
                        editorGlass = editorGlasses[this._findGlassOffset % editorGlasses.length];
                    }

                    if (this.draggedEditorGlass.value == null && editorGlass != null && editorGlass?.node != null) {
                        if (!editorGlass.node.active) break;
                        this.draggedEditorGlass.value = editorGlass;
                        let position = this.getComponent(UITransform).convertToNodeSpaceAR(worldPosition);
                        this.draggedOffset.set(position).subtract(editorGlass.node.position);
                        this.tryOpenGlassInspector(editorGlass.glassId.value);
                    }
                    if (this.draggedEditorGlass.value != null) {
                        let position = this.getComponent(UITransform).convertToNodeSpaceAR(worldPosition);
                        let targetPosition = v3(position).subtract(this.draggedOffset);
                        this._v30.set(targetPosition);
                        this.draggedEditorGlass.value.node.setPosition(this._v30);
                        this.glassInspectorMap.get(this.draggedEditorGlass.value.glassId.value)?.refreshGraphics();
                    }
                }
                break;
            }
            case 'touch-cancel':
            case 'touch-end':
            case 'touch-end-click': {
                let editorBlock = this.draggedEditorBlock.value;
                if (editorBlock != null) {
                    let zeroPosition = this.getZeroPosition(this.draggedEditorBlock.value.shapeId, this.draggedEditorBlock.value.node.position);
                    let targetIdx = this.getIdxByPos(zeroPosition);
                    if (targetIdx == null) { // 错误位置返回
                        this.setBlockToPositonId(editorBlock, editorBlock.posId.value);
                    } else {
                        this.setBlockToPositonId(editorBlock, targetIdx);
                        editorBlock.posId.value = targetIdx;
                    }
                    this.draggedEditorBlock.value = null;
                    this.remakeStageInfo();
                }
                let editorGlass = this.draggedEditorGlass.value;
                if (editorGlass != null) {
                    let zeroPosition = this.getZeroPosition(this.draggedEditorGlass.value.shapeId, this.draggedEditorGlass.value.node.position);
                    let targetIdx = this.getIdxByPos(zeroPosition);
                    if (targetIdx == null) { // 错误位置返回
                        this.setGlassToPositionId(editorGlass, editorGlass.posId.value);
                    } else {
                        this.setGlassToPositionId(editorGlass, targetIdx);
                        editorGlass.posId.value = targetIdx;
                    }
                    this.draggedEditorGlass.value = null;
                    this.remakeStageInfo();
                }
                // if (this._lastType == 'touch-start') {
                //     this._findGlassOffset++;
                // }
                break;
            }
        }

        if (type == 'touch-end-click') {
            let position = this.getComponent(UITransform).convertToNodeSpaceAR(worldPosition);
            let idx = this.getIdxByPos(position);
            this.handleClickIdx(idx);
        }
        this._lastType = type;
    }

    handleClickIdx(idx: number) {
        this.log('@handleClickIdx', idx);
        let edited = false;
        if (gm.editCtrlPressed.value) {
            // 如果点击玻璃的对应位置，则设定钉子颜色
            for (let [glassId, editorGlass] of this.glassMap) {
                if (!editorGlass.node.active) continue;
                let shapeIdxs = this.getShapeOccupyIdxs(editorGlass.shapeId, editorGlass.posId.value);
                for (let i = 0; i < shapeIdxs.length; i++) {
                    if (shapeIdxs[i] == idx) {
                        let nails = editorGlass.nails.value;
                        nails[i] = gm.editColorSelected.value;
                        editorGlass.nails.value = nails;
                        edited = true;
                    }
                }
            }
        }
        if (edited) {
            this.remakeStageInfo();
        }
    }

    getZeroPosition(shapeId: number, oriPos: Vec3) {
        let shapeInfo = shapeInfoMap.get(shapeId);
        let position = v3().set(oriPos);
        position.subtract3f(shapeInfo.center[1] * EditorStageView.Space.x, -shapeInfo.center[0] * EditorStageView.Space.y, 0);
        return position;
    }

    // getBlockZeroPosition(editorBlock: EditorBlock) {
    //     let shapeId = editorBlock.shapeId;
    //     let shapeInfo = shapeInfoMap.get(shapeId);
    //     let position = v3().set(editorBlock.node.position);
    //     position.subtract3f(shapeInfo.center[1] * EditorStageView.Space.x, -shapeInfo.center[0] * EditorStageView.Space.y, 0);
    //     return position;
    // }
    // getGlassZeroPosition(editorGlass: EditorGlass) {
    //     let shapeId = editorGlass.shapeId;
    //     let shapeInfo = shapeInfoMap.get(shapeId);
    //     let position = v3().set(editorGlass.node.position);
    //     position.subtract3f(shapeInfo.center[1] * EditorStageView.Space.x, -shapeInfo.center[0] * EditorStageView.Space.y, 0);
    //     return position;
    // }

    _sub_block_move_animation: Subscription = null;
    setBlockToPositonId(editorBlock: EditorBlock, posId: number) {
        this._sub_block_move_animation = new Observable(observer => {
            gm.inAnimation.value = true;
            let targetPosition = this.genShapePos(posId, editorBlock.shapeId);
            let _sub = TweenToStart(editorBlock.node, .1, { position: targetPosition }, { easing: 'quadInOut' }).subscribe({
                next: _ => {
                    this.blockInspectorMap.get(editorBlock.blockId.value)?.refreshGraphics();
                },
                complete: () => {
                    gm.inAnimation.value = false;
                    observer.nextAndComplete(null);
                }
            });
            return () => {
                _sub.unsubscribe();
            }
        }).subscribe();
    }
    setGlassToPositionId(editorGlass: EditorGlass, posId: number) {
        this._sub_block_move_animation = new Observable(observer => {
            gm.inAnimation.value = true;
            let targetPosition = this.genShapePos(posId, editorGlass.shapeId);
            let _sub = TweenToStart(editorGlass.node, .1, { position: targetPosition }, { easing: 'quadInOut' }).subscribe({
                next: _ => {
                    this.glassInspectorMap.get(editorGlass.glassId.value)?.refreshGraphics();
                },
                complete: () => {
                    gm.inAnimation.value = false;
                    observer.nextAndComplete(null);
                }
            });
            return () => {
                _sub.unsubscribe();
            }
        }).subscribe();
    }

    isOccupy(posId: number) {
        return this.blockMap.has(posId);
    }

    genBlockId() {
        let idsAlreadyHave = new Set();
        for (let [posId, node] of this.blockMap) {
            let id = node.getComponent(EditorBlock)?.blockId.value;
            if (id != null) {
                idsAlreadyHave.add(id);
            }
        }
        let newId = 0;
        let has = true;
        do {
            has = idsAlreadyHave.has(newId);
            if (!has) {
                return newId;
            }
            newId++;
        } while (has);
    }

    blockMap = new Map<number, EditorBlock>();
    addBlock(posIdx: number, shapeId: number, blockId?: number): EditorBlock {
        let blockNode = instantiate(this.editorBlockPrefab);
        blockNode.setParent(this.blockContainer);
        let block = blockNode.getComponent(EditorBlock);
        block.blockId.value = blockId ?? this.genBlockId();
        block.posId.value = posIdx;
        block.shapeId = shapeId;
        block.colorId = gm.editColorSelected.value;
        let blockPos = this.genShapePos(posIdx, shapeId);
        blockNode.setPosition(blockPos);
        this.blockMap.set(block.blockId.value, block);
        return block;
    }

    removeBlock(blockId: number) {
        let lastBlock = this.blockMap.get(blockId);
        if (lastBlock != null) {
            this.closeBlockInspector(lastBlock.blockId.value);
        }
        if (lastBlock?.node?.isValid) {
            lastBlock.node.destroy();
        }
        this.blockMap.delete(blockId);
    }

    genGlassId() {
        let idsAlreadyHave = new Set();
        for (let [posId, node] of this.glassMap) {
            let id = node.getComponent(EditorGlass)?.glassId.value;
            if (id != null) {
                idsAlreadyHave.add(id);
            }
        }
        let newId = 0;
        let has = true;
        do {
            has = idsAlreadyHave.has(newId);
            if (!has) {
                return newId;
            }
            newId++;
        } while (has);
    }

    glassMap = new Map<number, EditorGlass>();
    addGlass(posIdx: number, shapeId: number, glassId?: number): EditorGlass {
        let shapeInfo = shapeInfoMap.get(shapeId);
        let glassNode = instantiate(this.editorGlassPrefab);
        glassNode.setParent(this.glassContainer);
        let glass = glassNode.getComponent(EditorGlass);
        glass.glassId.value = glassId ?? this.genGlassId();
        glass.posId.value = posIdx;
        glass.shapeId = shapeId;
        glass.nails.value = Array.from({ length: shapeInfo.data.length }, (_, _idx) => gm.editColorSelected.value);
        let shapePos = this.genShapePos(posIdx, shapeId);
        glassNode.setPosition(shapePos);
        this.glassMap.set(glass.glassId.value, glass);
        gm.glassesInfoRefreshCommand.execute();
        return glass;
    }

    removeGlass(glassId: number) {
        let lastGlass = this.glassMap.get(glassId);
        if (lastGlass != null) {
            this.closeGlassInspector(lastGlass.glassId.value);
        }
        if (lastGlass?.node?.isValid) {
            lastGlass.node.destroy();
        }
        this.glassMap.delete(glassId);
        gm.glassesInfoRefreshCommand.execute();
    }

    findBlockByWorldPos(worldPosition: Vec3): EditorBlock {
        let position = this.blockContainer.getComponent(UITransform).convertToNodeSpaceAR(worldPosition);
        let idx = this.getIdxByPos(position);
        for (let [blockId, editorBlock] of this.blockMap) {
            let blockIdxs = this.getShapeOccupyIdxs(editorBlock.shapeId, editorBlock.posId.value);
            if (blockIdxs.includes(idx)) {
                return editorBlock;
            }
        }
    }

    findGlassByWorldPos(worldPosition: Vec3): EditorGlass[] {
        let position = this.blockContainer.getComponent(UITransform).convertToNodeSpaceAR(worldPosition);
        let idx = this.getIdxByPos(position);
        let result: EditorGlass[] = [];
        for (let [glassId, editorGlass] of this.glassMap) {
            let shapeIdxs = this.getShapeOccupyIdxs(editorGlass.shapeId, editorGlass.posId.value);
            if (shapeIdxs.includes(idx)) {
                result.push(editorGlass);
            }
        }
        return result;
    }

    getShapeOccupyIdxs(shapeId: number, posId: number) {
        let idxs: number[] = [];
        let rcPos = this.getRowColPos(posId);
        let shapeInfo = shapeInfoMap.get(shapeId);
        for (let i = 0; i < shapeInfo.data.length; i++) {
            let offset = [rcPos[0], rcPos[1]];
            let [y, x] = shapeInfo.data[i];
            offset[0] += y;
            offset[1] += x;
            let idx = this.getIdxByRowCol(offset);
            idxs.push(idx);
        }
        return idxs;
    }

    // 平移地图
    draggedStage = false;
    startDragPosition = v3();
    startStagePosition = v3();
    handleTouchInEditModePanStage(param: { type: string, eventTouch: EventTouch, worldPosition: Vec3 }) {
        let { type, eventTouch, worldPosition } = param;
        switch (type) {
            case 'touch-start':
            case 'touch-move': {
                if (this.draggedStage == false) {
                    this.draggedStage = true;
                    this.startDragPosition.set(worldPosition);
                    this.startStagePosition.set(this.node.position);
                }
                if (this.draggedStage) {
                    let dragVector = v3(worldPosition).subtract(this.startDragPosition);
                    let targetVector = v3(this.startStagePosition).add(dragVector);
                    this.node.setPosition(targetVector);
                }
                break;
            }
            case 'touch-cancel':
            case 'touch-end':
            case 'touch-end-click': {
                this.draggedStage = false;
                break;
            }
        }
    }


    // block inspector
    tryOpenBlockInspector(blockId: number) {
        let editorBlock = this.blockMap.get(blockId);
        if (editorBlock != null) {
            this.closeAllInspectors();

            if (this.blockInspectorMap.has(editorBlock.blockId.value)) return;

            let inspectorNode = instantiate(this.editorBlockInspectorPrefab);
            inspectorNode.setParent(this.inspectorContainer);

            let inspector = inspectorNode.getComponent(EditorBlockInspector);
            inspector.editorStageView = this;
            inspector.editorBlock.value = editorBlock;

            let containerSize = this.inspectorContainer.getComponent(UITransform).contentSize;
            let inspectorSize = this.node.getComponent(UITransform).contentSize;
            let pos = v3(
                Math.max(-1000, -containerSize.width / 2 + inspectorSize.width / 2) + editorBlock.blockId.value * 20,
                -containerSize.height / 2 + inspectorSize.height / 2 + editorBlock.blockId.value * 20,
                0
            );
            inspectorNode.setPosition(pos);
            this.blockInspectorMap.set(editorBlock.blockId.value, inspector);
        }
    }

    closeAllBlockInspectors(all = false) {
        let inspectors: EditorBlockInspector[] = [];
        for (let [idx, inspector] of this.blockInspectorMap) {
            if (all) {
                inspectors.push(inspector);
            } else {
                if (!inspector.pined.value) {
                    inspectors.push(inspector);
                }
            }
        }
        inspectors.forEach(_ => this.closeBlockInspector(_.editorBlock.value.blockId.value));
    }

    closeBlockInspector(blockId: number) {
        let inspector = this.blockInspectorMap.get(blockId);
        if (inspector?.node?.isValid) {
            inspector.node.destroy();
        }
        this.blockInspectorMap.delete(blockId);
    }

    // glass inspector
    tryOpenGlassInspector(glassId: number) {
        let editorGlass = this.glassMap.get(glassId);
        if (editorGlass != null) {
            this.closeAllInspectors();

            if (this.glassInspectorMap.has(editorGlass.glassId.value)) return;

            let inspectorNode = instantiate(this.editorGlassInspectorPrefab);
            inspectorNode.setParent(this.inspectorContainer);

            let inspector = inspectorNode.getComponent(EditorGlassInspector);
            inspector.editorStageView = this;
            inspector.editorGlass.value = editorGlass;

            let containerSize = this.inspectorContainer.getComponent(UITransform).contentSize;
            let inspectorSize = this.node.getComponent(UITransform).contentSize;
            let pos = v3(
                Math.max(-1000, -containerSize.width / 2 + inspectorSize.width / 2) + editorGlass.glassId.value * 20,
                -containerSize.height / 2 + inspectorSize.height / 2 + editorGlass.glassId.value * 20,
                0
            );
            inspectorNode.setPosition(pos);
            this.glassInspectorMap.set(editorGlass.glassId.value, inspector);
            nc.post('glass_inspector_change', null);
        }
    }

    closeAllGlassInspectors(all = false) {
        let inspectors: EditorGlassInspector[] = [];
        for (let [idx, inspector] of this.glassInspectorMap) {
            if (all) {
                inspectors.push(inspector);
            } else {
                if (!inspector.pined.value) {
                    inspectors.push(inspector);
                }
            }
        }
        inspectors.forEach(_ => this.closeGlassInspector(_.editorGlass.value.glassId.value));
    }

    closeGlassInspector(glassId: number) {
        let inspector = this.glassInspectorMap.get(glassId);
        if (inspector?.node?.isValid) {
            inspector.node.destroy();
        }
        this.glassInspectorMap.delete(glassId);
        nc.post('glass_inspector_change', null);
    }

    closeAllInspectors(all = false) {
        this.closeAllBlockInspectors(all);
        this.closeAllGlassInspectors(all);
    }

    glassExplorer: EditorGlassExplorer = null;
    tryOpenGlassExplorer() {
        if (this.glassExplorer == null) {
            let explorerNode = instantiate(this.editorGlassExplorerPrefab);
            explorerNode.setParent(this.inspectorContainer);

            let explorer = explorerNode.getComponent(EditorGlassExplorer);
            explorer.editorStageView = this;

            let containerSize = this.inspectorContainer.getComponent(UITransform).contentSize;
            let explorerSize = this.node.getComponent(UITransform).contentSize;
            let pos = v3(
                Math.max(-1000, -containerSize.width / 2 + explorerSize.width / 2) - 400,
                -containerSize.height / 2 + explorerSize.height / 2,
                0
            );
            explorerNode.setPosition(pos);
            this.glassExplorer = explorer;
            gm.glassesInfoRefreshCommand.execute();
        }
    }
    closeGlassExplorer() {
        if (this.glassExplorer?.node?.isValid) {
            this.glassExplorer.node.destroy();
            this.glassExplorer = null;
        }
    }


    _oldStageInfoStrList: string[] = [];

    remakeStageInfo() {
        // 保存
        this._oldStageInfoStrList.push(JSON.stringify(this.stageInfo));
        if (this._oldStageInfoStrList.length > 100) this._oldStageInfoStrList.shift();

        this.stageInfo.blocks = [];
        for (let [blockId, editorBlock] of this.blockMap) {
            let block = {
                id: editorBlock.blockId.value,
                pos: editorBlock.posId.value,
                shape: editorBlock.shapeId,
                color: editorBlock.colorId,
            };
            this.stageInfo.blocks.push(block);
        }
        this.stageInfo.blocks.sort((a, b) => a.id - b.id);

        this.stageInfo.glasses = [];
        for (let [glassId, editorGlass] of this.glassMap) {
            let glass = {
                id: editorGlass.glassId.value,
                pos: editorGlass.posId.value,
                shape: editorGlass.shapeId,
                nails: editorGlass.nails.value,
                layer: editorGlass.layer.value,
                lock: 0,
            }
            this.stageInfo.glasses.push(glass);
        }
        this.stageInfo.glasses.sort((a, b) => a.layer - b.layer);
        gm.glassesInfoRefreshCommand.execute();
    }

    undo() {
        if (this._oldStageInfoStrList.length > 0) {
            let stageInfo = JSON.parse(this._oldStageInfoStrList.pop());
            gm.loadStageCommand.execute(stageInfo);
        }
    }

    reorderGlasses() {
        let list: EditorGlass[] = [];
        for (let [glassId, editorGlass] of this.glassMap) {
            list.push(editorGlass);
            editorGlass.node.removeFromParent();
        }
        list.sort((a, b) => a.layer.value - b.layer.value);
        for (let editorGlass of list) {
            editorGlass.node.setParent(this.glassContainer);
        }
    }

}

